package TrackModel;

import java.util.ArrayList;

import application.Main;
import TrainModel.TrainModel;
import Utilities.Message;


/**
 * @author Jordan
 *TrackModel is the top-level class for the Track Model module. A TrackModel object is initialized by
 *the CTC Office and the Red and Green lines are built from the redline.txt file and the greenline.txt
 *file, respectively. The TrackModel is mainly responsible for storing Line objects for all of the lines
 *in the system, as well as having an inner-class for a Yard object where trains originate.
 */
public class TrackModel
{
	public Yard yard;
	public Line redLine, greenLine;
	public static Line testLine;
	public ArrayList<Line> theLines;
	
	/**
	 * Constructor for the TrackModel object. Initializes an ArrayList to hold Line objects, initializes the Yard object
	 * for the system, then initializes the Red and Green lines and adds them to the ArrayList.
	 */
	public TrackModel()
	{
		theLines = new ArrayList<Line>();
		yard = new Yard();
		greenLine = new Line("Green", "greenline.txt");
		redLine = new Line("Red", "redline.txt");
		theLines.add(greenLine);
		theLines.add(redLine);
	}
	
	/**
	 * Function responsible for updating the Track Model's state. First checks every Track Controller on every Line
	 * for messages, then propagates those messages along the track to any Train Models that are on the track.
	 * Also updates the location of all of the Train Models on the track
	 */
	public void tick()
	{
		
		Line theLine;
		ArrayList<ControllerBlock> theControllers;
		for (int i = 0; i < theLines.size(); i++) //Iterate through each Line in the system
		{
			theLine = theLines.get(i);
			theControllers = theLine.getControllers();
			for (int j = 0; j < theControllers.size(); j++) //Iterate through every Track Controller on that line
			{
				ControllerBlock theController = theControllers.get(j);
				while (theController.hasMessage()) //Get all of the messages from that Track Controller
				{
					Message theMessage = theController.getMessage();
					System.out.println("TrackModel: Pulled a message from Controller "+ j);
					ArrayList<Block> theBlocks = theController.getControllerBlocks();
					for (int k = 0; k < theBlocks.size(); k++) //Pass the message along that Track Controller's blocks
					{
						Block theBlock = theBlocks.get(k);
						if (theBlock.hasTrain) //If a Train Model is found, pass the message to that Train Model
						{
							if(theBlock.getTrain() != null) 
							{
								System.out.println("TrackModel: Sent a message to Train "+theBlock.getTrain().getTrainID()+" from block "+theBlock.getBlockID());
								theBlock.getTrain().setMessage(theMessage);
							}
						}
					}
				}
			}
		}
		
		// update train locations
		ArrayList<Line.Train> theTrains;
		for (int i = 0; i < theLines.size(); i++) //Iterate through every Line in the system
		{
			theLine = theLines.get(i);
			theTrains = theLine.getTrains();
			for (int j = 0; j < theTrains.size(); j++) //Update the location of every Train Model on that Line
			{
				theTrains.get(j).updateLocation(Main.lTick);
			}
		}
	}
	
	/**Returns an ArrayList of Line objects containing all of the Lines in the system
	 * @return ArrayList<Line> theLines
	 */
	public ArrayList<Line> getLines()
	{
		return theLines;
	}
	
	/**Takes a String variable containing the name of the Line being requested, then
	 * returns the requested Line object
	 * @param l - The name of the Line being requested
	 * @return Line
	 */
	public Line getLine(String l)
	{
		if (l.equals("Green"))
			return theLines.get(0);
		else if (l.equals("Red"))
			return theLines.get(1);
		else
			return theLines.get(2);
	}
	
	/**
	 * @author Jordan
	 *Inner class that models the Yard of the railroad system. Keeps an ArrayList of type Train containing all of the
	 *Train objects currently in the yard. Also the place where Train and Train Model objects are created and first sent
	 *out onto the track. 
	 */
	public class Yard 
	{
		private int numTrains;
		private int[] coordinates = {555, 277};
		private ArrayList<TrainModel> trainsInYard;
		
		/**Constructor for the Yard class. Initializes the number of trains in the yard to 0 and initializes the ArrayList
		 * that will store all of the Train Model's until they are routed onto the track.
		 * 
		 */
		public Yard()
		{
			numTrains = 0;
			trainsInYard = new ArrayList<TrainModel>();
		}
		
		/**Returns a two-element int array {x, y} containing the x and y coordinates of the Yard
		 * @return int array containing the coordinates of the Yard
		 */
		public int[] getCoordinates()
		{
			return coordinates;
		}
		
		/**Method called by the CTC Office to create a Train Model object and stores it in the Yard until it is ready to be
		 * routed.
		 * @param trainID - the ID for the Train Model object to be created
		 * @param tickLength - The system tick length that the Train Model object will receive
		 * @return Returns the newly created Train Model object to the CTC Office
		 */
		public TrainModel createTrain(int trainID, long tickLength)
		{
			numTrains++;
			TrainModel theTrain = new TrainModel(trainID, tickLength);
			theTrain.toggleConductor();
			trainsInYard.add(theTrain);
			return theTrain;
		}
		
		/**Takes the Train Model to be routed and the name of the Line that the Train Model is to be
		 * routed onto as a String, then finds the appropriate Train Model object in the Yard and 
		 * routes it to the correct Line.
		 * @param theTrain - Train Model object to be routed onto the track
		 * @param line - The Line that the Train Model is to be routed onto
		 */
		public void sendTrain(TrainModel theTrain, String line)
		{
			int id = theTrain.getTrainID();
			TrainModel yardTrain;
			for (int i = 0; i < trainsInYard.size(); i++) //Find the Train Model object to be routed
			{
				yardTrain = trainsInYard.get(i);
				if (yardTrain.getTrainID() == id)
				{
					trainsInYard.remove(i); //Remove the Train Model from the Yard
					break;
				}
			}
			if (line.equals("Red")) //Route the Train Model to the Red Line
			{
				theTrain.setCurrentBlock(redLine.getBlock(9));
				redLine.addTrain(theTrain, 9, 10);
			}
			else if (line.equals("Green")) //Route the Train Model to the Green Line
			{
				theTrain.setCurrentBlock(greenLine.getBlock(63));
				greenLine.addTrain(theTrain, 63, 62);
			}
			else //Route the Train Model to the Test Line
			{
				theTrain.setCurrentBlock(testLine.getBlock(1));
				testLine.addTrain(theTrain, 1, 0);
			}
			numTrains--;
		}
		
		/**Returns the first Block of the requested Line. Used by the CTC Office to start
		 * navigating the Lines.
		 * @param line - Name of the Line that the first Block is being requested from
		 * @return Returns a Block object that is the first Block that can be travelled to
		 * 			from the Yard
		 */
		public Block getNextBlock(String line)
		{
			if (line.equals("Red"))
				return redLine.getBlock(9);
			else if (line.equals("Green"))
				return greenLine.getBlock(63);
			else
				return testLine.getBlock(1);
		}
		
		/**Returns the first StationBlock of the requested Line. Used by the CTC Office to
		 * start routing Train Models from the Yard
		 * @param line - Name of the Line that the first Station is being requested from
		 * @return Returns a StationBlock object that is the first station from the Yard
		 */
		public StationBlock getNextStation(String line)
		{
			if (line.equals("Red"))
				return (StationBlock)redLine.getBlock(7);
			else
				return (StationBlock)greenLine.getBlock(65);
		}
		
		/**Method to receive a Train Model from the track and place it in the Yard
		 * @param theTrain - The Train Model that is entering the Yard
		 */
		public void receiveTrain(TrainModel theTrain)
		{
			numTrains++;
			trainsInYard.add(theTrain);
		}
		
		/**
		 * @return Returns the number of Train Models in the Yard as an int
		 */
		public int getNumTrains()
		{
			return numTrains;
		}
	}
}